package com.sina.interfaces.manage.config;

import com.sina.interfaces.core.util.FileUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * Description:  shiro的配置   从启动类中抽取出来作为单独配置。
 * 段浩杰   2017-11-03-11:05
 */
@Configuration
public class ShiroConfiguration {

    //注册shiro，并配置拦截url
    @Bean
    public FilterRegistrationBean shiroFilterRegistrationBean() {

        DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();

        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filterProxy);
        registrationBean.addInitParameter("targetFilterLifecycle", "true");
        registrationBean.setName("shiroFilter");
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    //定义shiro的过滤器，指定过滤的规则以及url
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setLoginUrl("/admin/login");
        factoryBean.setSuccessUrl("/home");
        factoryBean.setUnauthorizedUrl("/unauthorized");
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setFilters(filters());
        factoryBean.setFilterChainDefinitions(FileUtils.getTextContent("classpath:config/shrio.init"));
        return factoryBean;
    }

    /*配置自定义logout-------start*/
    public LogoutFilter logout() {
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/admin/login");
        return logoutFilter;
    }

    public Map<String, Filter> filters() {
        Map<String, Filter> map = new HashMap<>();
        map.put("logout", logout());
        return map;
    }
    /*配置自定义logout-------end*/

    //使用DefaultWebSecurityManager，为其增加realm、cacheManage、sessionManage。
    @Bean
    public DefaultWebSecurityManager shiroWebSecurityManager(
            @Qualifier("shiroRealm") Realm shiroRealm,
            @Qualifier("cacheManager") EhCacheManager cacheManager,
            @Qualifier("sessionManager") DefaultWebSessionManager sessionManager,
            @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager
    ) {

        DefaultWebSecurityManager manager = new DefaultWebSecurityManager(shiroRealm);
        manager.setCacheManager(cacheManager);//放入到SecurityManager中，他会配置到认证授权中
        manager.setSessionManager(sessionManager);
        manager.setRememberMeManager(rememberMeManager);
        return manager;
    }


    @Bean
    public SimpleCookie sessionIdCookie() {
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        //单位秒;maxAge=-1表示浏览器关闭时失效此Cookie；
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }


    /**
     * cookie对象;
     * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //记住我cookie有效时间一周 ,单位秒;
        simpleCookie.setMaxAge(25200);
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(@Qualifier("rememberMeCookie") SimpleCookie rememberMeCookie) {
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie);
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }


    //配置sessionDao
    //指定缓存
    @Bean
    public SessionDAO sessionDAO(EhCacheManager cacheManager) {
        EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
        enterpriseCacheSessionDAO.setActiveSessionsCacheName("sessionCache");//指定缓存的名字
        enterpriseCacheSessionDAO.setCacheManager(cacheManager); //配置ehcache
        return enterpriseCacheSessionDAO;
    }

    //配置sessionManager，使用默认的sessionDao。
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager sessionManager(SessionDAO sessionDAO) {
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        defaultWebSessionManager.setGlobalSessionTimeout(600000);//设置超时时间
        defaultWebSessionManager.setDeleteInvalidSessions(true);//清除无效的session
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);//定时清除无效的session
        defaultWebSessionManager.setSessionDAO(sessionDAO);//设置sessionDAO
        return defaultWebSessionManager;
    }


    //配置缓存
    @Bean
    public EhCacheManager cacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:config/shiro-ehcache.xml");//指定ehcache配置文件的地址
        return cacheManager;
    }

    /**
     * HashedCredentialsMatcher，这个类是为了对密码进行编码的，
     * 防止密码在数据库里明码保存，当然在登陆认证的时候，
     * 这个类也负责对form里输入的密码进行编码。
     * <p>
     * 数据库密码存储时使用的加密方式要和配置文件中配置的方式相一致。
     * 如以上配置，那么加密方式如下：
     * <p>
     * String password =  new SimpleHash("md5","123456",null,2).toHex();
     */
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");//指定Hash算法为MD5
        credentialsMatcher.setHashIterations(2);//加密次数
        credentialsMatcher.setStoredCredentialsHexEncoded(true);//指定Hash散列值使用Hex加密存储
        return credentialsMatcher;
    }

    //自定义realm，用来指定授权和认证。
    @Bean
    public Realm shiroRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher) {
        ShiroRealm realm = new ShiroRealm();//添加自定义的realm
        realm.setAuthenticationCacheName("authenticationCache");//指定认证缓存名字
        realm.setAuthorizationCacheName("authorizationCache");//指定授权缓存名字
        realm.setCredentialsMatcher(hashedCredentialsMatcher);//密码加密
        //realm.setPermissionResolver();//设置自定义权限字符串表示
        return realm;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * AuthorizationAttributeSourceAdvisor，shiro里实现的Advisor类，
     * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}
